 /*
 * Copyright (C) 2022, KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */
 //
// Created by hxf on 22-8-2.
//

#include "side-bar-application.h"

#include <QCoreApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QWindow>
#include <QDebug>
#include <QCommandLineOption>
#include <QCommandLineParser>
#include <QPainterPath>
#include <QQuickWindow>
#include <QThread>
#include <QOpenGLContext>
#include <QSurfaceFormat>

#include "global-settings.h"
#include "shortcut-model-manager.h"
#include "screen-monitor.h"
#include "color-helper.h"
#include "account-information.h"
#include "notification-manager.h"
#include "layout-config.h"
#include "hand-gesture-helper.h"
#include "date-time-utils.h"
#include "weather-helper.h"
#include "shadow-item.h"
#include "window-blur-helper.h"
#include "app-manager.h"
#include "sidebar-window.h"

using namespace  Sidebar;

SideBarApplication::SideBarApplication(const QString &appId, int &argc, char **argv) : QtSingleApplication(appId, argc, argv)
{
    if (!isRunning()) {
        //load translations.
        m_translator = new QTranslator(this);
        try {
            if(! m_translator->load(QString(TRANSLATION_FILE_DIR) + "/ukui-sidebar_" + QLocale::system().name() + ".qm")) throw - 1;
            SideBarApplication::installTranslator(m_translator);
        } catch(...) {
            qWarning() << "Load translations file" << QLocale::system().name() << "failed!";
        }

        createSystray();
        waitTime();

        connect(this, &QtSingleApplication::messageReceived, [=](const QString &msg) {
            this->parseCommand(msg, true);
        });
    } else {
        parseCommand(SideBarApplication::arguments().join(" ").toUtf8(), false);
    }
}

SideBarApplication::~SideBarApplication()
{
    if (m_trayIconMenu) {
        delete m_trayIconMenu;
    }
}

void SideBarApplication::initPublicObjects()
{
    GlobalSettings::globalInstance();
    ColorHelper::getInstance();
    ScreenMonitor::getInstance();
    LayoutConfig::getInstance();
    SidebarGeometryController::getInstance();
    HandGestureHelper::getInstance();
    SidebarWindowHelper::instance();

    // 侧边栏启动信号转接桥
    m_sidebarEventBridge = new SidebarEventBridge(this);
    connect(m_sidebarEventBridge, &SidebarEventBridge::sidebarRequestClearRedPoint,
            this, &SideBarApplication::changeIconBack);

    m_notificationEventBridge = new NotificationEventBridge(this);
    connect(m_notificationEventBridge, &NotificationEventBridge::notificationCenterRequestClearRedPoint,
            this, &SideBarApplication::changeIconBack);
}

 bool SideBarApplication::checkOpenGLVersion()
{
    QOpenGLContext glContext;
    if (glContext.create()) {
        QSurfaceFormat surfaceFormat = glContext.format();
        qDebug() << "openGL profile:" << surfaceFormat.profile();
        qDebug() << "openGL Version:" << surfaceFormat.majorVersion() << surfaceFormat.minorVersion();

        if (surfaceFormat.profile() == QSurfaceFormat::NoProfile) {
            return false;
        }

        // opengl 3.3及以上
        return (surfaceFormat.majorVersion() >= 3 && surfaceFormat.minorVersion() >= 3);
    }
    qDebug() << "QOpenGLContext create: false";
    return false;
}

void SideBarApplication::loadQML()
{
    if (!checkOpenGLVersion()) {
        QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
    }
    bool isOpenGLEnv = QQuickWindow::sceneGraphBackend() == "";
    qDebug() << "loadQML isOpenGLEnv:" << isOpenGLEnv << QQuickWindow::sceneGraphBackend();

    auto notificationManager = new Notify::NotificationManager(this);
    auto dateTimeUtils = new SideBar::DateTimeUtils(this);

    connect(notificationManager, &Notify::NotificationManager::iHaveUnreadMessage,
            this, &SideBarApplication::changeIcon);

    //控制中心
    m_shortcutEngine = new QQmlApplicationEngine(this);
    connect(m_shortcutEngine, &QQmlApplicationEngine::objectCreated,
            this, &SideBarApplication::checkObject, Qt::QueuedConnection);

    UkuiShortcut::ShortcutModelManager* modelManager = new UkuiShortcut::ShortcutModelManager(this);
    connect(modelManager, &UkuiShortcut::ShortcutModelManager::requestExecAction,
            this, &SideBarApplication::execShortcutAction);

    m_shortcutEngine->rootContext()->setContextProperty("isOpenGLEnv", isOpenGLEnv);
    m_shortcutEngine->rootContext()->setContextProperty("isNotificationCenter", false);
    m_shortcutEngine->rootContext()->setContextProperty("dateTimeUtils", dateTimeUtils);
    m_shortcutEngine->rootContext()->setContextProperty("notificationManager", notificationManager);
    m_shortcutEngine->rootContext()->setContextProperty("sidebarEventBridge", m_sidebarEventBridge);
    m_shortcutEngine->rootContext()->setContextProperty("colorHelper", ColorHelper::getInstance());
    m_shortcutEngine->rootContext()->setContextProperty("modelManager", modelManager);
    m_shortcutEngine->rootContext()->setContextProperty("weatherHelper", new WeatherHelper(this));
    m_shortcutEngine->rootContext()->setContextProperty("screenMonitor", ScreenMonitor::getInstance());
    m_shortcutEngine->rootContext()->setContextProperty("layoutConfig", LayoutConfig::getInstance());
    m_shortcutEngine->rootContext()->setContextProperty("handGestureHelper", HandGestureHelper::getInstance());
    m_shortcutEngine->load(m_shortcutQmlFile);

    //通知中心
    m_notificationEngine = new QQmlApplicationEngine(this);
    connect(m_notificationEngine, &QQmlApplicationEngine::objectCreated,
            this, &SideBarApplication::checkObject, Qt::QueuedConnection);

    m_notificationEngine->rootContext()->setContextProperty("isOpenGLEnv", isOpenGLEnv);
    m_notificationEngine->rootContext()->setContextProperty("isNotificationCenter", true);
    m_notificationEngine->rootContext()->setContextProperty("dateTimeUtils", dateTimeUtils);
    m_notificationEngine->rootContext()->setContextProperty("notificationManager", notificationManager);
    m_notificationEngine->rootContext()->setContextProperty("colorHelper", ColorHelper::getInstance());
    m_notificationEngine->rootContext()->setContextProperty("screenMonitor", ScreenMonitor::getInstance());
    m_notificationEngine->rootContext()->setContextProperty("layoutConfig", LayoutConfig::getInstance());
    m_notificationEngine->rootContext()->setContextProperty("handGestureHelper", HandGestureHelper::getInstance());
    m_notificationEngine->rootContext()->setContextProperty("notificationEventBridge",m_notificationEventBridge);
    m_notificationEngine->load(m_notificationQmlFile);

    //状态栏（响应下划动作）
    m_statusBarEngine = new QQmlApplicationEngine(this);
    connect(m_statusBarEngine, &QQmlApplicationEngine::objectCreated,
            this, &SideBarApplication::checkObject, Qt::QueuedConnection);

    m_statusBarEngine->rootContext()->setContextProperty("isOpenGLEnv", isOpenGLEnv);
    m_statusBarEngine->rootContext()->setContextProperty("screenMonitor", ScreenMonitor::getInstance());
    m_statusBarEngine->rootContext()->setContextProperty("handGestureHelper", HandGestureHelper::getInstance());
    m_statusBarEngine->rootContext()->setContextProperty("layoutConfig", LayoutConfig::getInstance());
    m_statusBarEngine->load(m_statusBarQmlFile);

    //屏幕右侧响应左划动作
    m_right2LeftSwipeEngine = new QQmlApplicationEngine(this);
    connect(m_right2LeftSwipeEngine, &QQmlApplicationEngine::objectCreated,
            this, &SideBarApplication::checkObject, Qt::QueuedConnection);

    m_right2LeftSwipeEngine->rootContext()->setContextProperty("isOpenGLEnv", isOpenGLEnv);
    m_right2LeftSwipeEngine->rootContext()->setContextProperty("screenMonitor", ScreenMonitor::getInstance());
    m_right2LeftSwipeEngine->rootContext()->setContextProperty("handGestureHelper", HandGestureHelper::getInstance());
    m_right2LeftSwipeEngine->load(m_right2LeftSwipeQmlFile);
}

void SideBarApplication::checkObject(QObject *object, const QUrl &url)
{
    if (!object && (url == m_shortcutQmlFile
                 || url == m_notificationQmlFile
                 || url == m_statusBarQmlFile
                 || url == m_right2LeftSwipeQmlFile)) {
        QCoreApplication::exit(-1);
    }
}

void SideBarApplication::parseCommand(const QString &msg, bool isPrimary)
{
    QCommandLineParser parser;

    QCommandLineOption sidebarState({"S", "state"}, QObject::tr("Show the current state of the sidebar."));
    QCommandLineOption sidebarShow({"s", "show"}, QObject::tr("There are two options, 'notify' and 'control'."), "option");
    QCommandLineOption sidebarQuit({"q", "quit"}, QObject::tr("Quit sidebar."));

    parser.addOption(sidebarState);
    parser.addOption(sidebarShow);
    parser.addOption(sidebarQuit);

    if (isPrimary) {
        parser.parse(msg.split(" "));

        if (parser.isSet(sidebarState)) {
            //state

        } else if (parser.isSet(sidebarShow)) {
            //show
            if(parser.value(sidebarShow) == "notify") {
                if (!m_notificationEventBridge) {
                    qWarning() << "The sidebar is not running or there is an error, please restart 'ukui-sidebar'";
                    return;
                }
                m_notificationEventBridge->notificationShow();

            } else if(parser.value(sidebarShow) == "control") {
                if (!m_sidebarEventBridge) {
                    qWarning() << "The sidebar is not running or there is an error, please restart 'ukui-sidebar'";
                    return;
                }
                m_sidebarEventBridge->activateSidebar();
            }

        } else if (parser.isSet(sidebarQuit)) {
            SideBarApplication::quit();
        }

    } else {
        // 检查命令行参数是否匹配，选项parse为true，但是命令选项可能为空
        if (parser.parse(arguments()) && !parser.optionNames().isEmpty()) {
            // sidebar主程序只接受 S, s, q 三个类别的命令
            this->sendMessage(msg);

        } else {
            QCommandLineOption helpOption = parser.addHelpOption();
            QCommandLineOption versionOption = parser.addVersionOption();

            parser.parse(arguments());
            if (parser.isSet(versionOption)) {
                parser.showVersion();
            } else {
                if (!parser.unknownOptionNames().isEmpty()) {
                    qDebug() << "Unknown options:" << parser.unknownOptionNames();
                }
                parser.showHelp();
            }
        }
    }
}

void SideBarApplication::registerItems()
{
    const char uri[] = "org.ukui.sidebar.core";
    SidebarWindowDefineModule::defineModules(uri, 1, 0);
    // 公用部分 包括自定义图标item
    qmlRegisterType<Sidebar::ShadowItem>("org.ukui.sidebar.core", 1, 0, "ShadowItem");
    qmlRegisterType<Sidebar::SettingMonitor>("org.ukui.sidebar.core", 1, 0, "SettingMonitor");
    qmlRegisterType<Sidebar::LayoutHelper>("org.ukui.sidebar.core", 1, 0, "LayoutHelper");
    qmlRegisterType<Sidebar::WindowBlurHelper>("org.ukui.sidebar.core", 1, 0, "WindowBlurHelper");
    qmlRegisterUncreatableType<UkuiShortcut::Color>("org.ukui.sidebar.core", 1, 0, "PluginColorRole", "Enum");
    qmlRegisterUncreatableType<Sidebar::LayoutComponent>("org.ukui.sidebar.core", 1, 0, "LayoutComponent", "Enum");

    // 控制中心专用部分
    qmlRegisterType<UkuiShortcut::PowerButton>("org.ukui.sidebar.shortcut.utils", 1, 0, "PowerButton");
    qmlRegisterType<UkuiShortcut::AccountInformation>("org.ukui.sidebar.shortcut.utils", 1, 0, "AccountInformation");

    // 控制中心: 注册类用于在qml访问枚举变量
    qmlRegisterUncreatableType<UkuiShortcut::ValueType>("org.ukui.sidebar.shortcut.core", 1, 0, "ValueType", "Enum");
    qmlRegisterUncreatableType<UkuiShortcut::PluginMetaType>("org.ukui.sidebar.shortcut.core", 1, 0, "ShortcutPlugin", "Enum");

    // 通知中心专用部分
    qmlRegisterType<Notify::NotificationManager>("org.ukui.sidebar.notify.core", 1, 0, "NotificationManager");

    //注册元类型 用于信号与槽，与qml交互
    qRegisterMetaType<UkuiShortcut::Color::ColorRole>("Color::ColorRole");
    qRegisterMetaType<UkuiShortcut::StatusInfo::MenuItem>("StatusInfo::MenuItem");
    qRegisterMetaType<Sidebar::LayoutComponent::Component>("LayoutComponent::Component");
    qRegisterMetaType<UkuiShortcut::PluginMetaType::Action>("PluginMetaType::Action");
    qRegisterMetaType<UkuiShortcut::PluginMetaType::PluginType>("PluginMetaType::PluginType");
    qRegisterMetaType<UkuiShortcut::PluginMetaType::SystemMode>("PluginMetaType::SystemMode");
    qRegisterMetaType<Notify::Message>("Message");
}

void SideBarApplication::waitTime()
{
    m_timer = new QTimer(this);
    connect(m_timer, &QTimer::timeout, this, &SideBarApplication::startSidebar);
    m_timer->start(1);
}

//系统托盘
void SideBarApplication::createActionForTrayIcon()
{
    if (!m_trayIconMenu) {
        return;
    }
    QAction* open = new QAction(QObject::tr("Open"), m_trayIconMenu);
    connect(open, &QAction::triggered, this, &SideBarApplication::OpenSidebarSlots);

    QAction* openSetUp = new QAction(QIcon::fromTheme("document-page-setup-symbolic", QIcon(SETTING_ICON)), tr("Set up notification center"), m_trayIconMenu);
    connect(openSetUp, &QAction::triggered, this, &SideBarApplication::OpenControlCenterSettings);

    m_trayIconMenu->addAction(open);
    m_trayIconMenu->addAction(openSetUp);

    if (!m_trayIcon) {
        return;
    }
    connect(m_trayIcon, &QSystemTrayIcon::activated, m_sidebarEventBridge, &SidebarEventBridge::activateSidebar);
}

//注册托盘、创建托盘菜单
void SideBarApplication::createSystray()
{
    int intervalTime = 100, registerCount = 0;
    while (!QSystemTrayIcon::isSystemTrayAvailable() && registerCount <= 5) {
        ++registerCount;
        intervalTime *=2;
        QThread::msleep(intervalTime);
    }

    m_trayIconMenu = new QMenu();
    if (m_trayIconMenu == nullptr) {
        qWarning() << "Allocate space trayIconMenu failed";
        return ;
    }

    m_trayIcon = new QSystemTrayIcon(this);
    if (nullptr == m_trayIcon) {
        qWarning()<< "Allocate space trayIcon failed";
        return ;
    }

    m_trayIcon->setIcon(QIcon::fromTheme("ukui-tool-symbolic", QIcon(TRAY_ICON)));
    m_trayIcon->setContextMenu(m_trayIconMenu);
    m_trayIcon->setToolTip(tr("ukui-sidebar"));
    m_trayIcon->setVisible(true);
}


void SideBarApplication::startSidebar()
{
    m_timer->stop();

    SideBarApplication::registerItems();

    initPublicObjects();
    loadQML();

    createActionForTrayIcon();
    // 初始化侧边栏dbus接口
    m_sidebarDbusService = new SidebarDbusService(this);

    // 第一次启动时，解析命令行参数
    parseCommand(SideBarApplication::arguments().join(" ").toUtf8(), true);
}

void SideBarApplication::OpenSidebarSlots()
{
    m_sidebarEventBridge->sidebarOnlyShow();
}

/* 打开控制中心的通知中心 */
void SideBarApplication::OpenControlCenterSettings()
{
    if (!AppManager::getInstance(this)->LaunchAppWithArguments("ukui-control-center.desktop", {"-m", "Notice"})) {
        QProcess::startDetached("ukui-control-center -m Notice");
    }
}

void SideBarApplication::changeIcon()
{
    if (!m_trayIcon) {
        return;
    }
    if ((!m_sidebarEventBridge->getSidebarState()) && (!m_notificationEventBridge->getNotificationCenterState())) {
        m_trayIcon->setIcon(QIcon::fromTheme("ukui-tool-box-null-symbolic", QIcon(TRAY_NULL_ICON)));
    }
}

void SideBarApplication::changeIconBack()
{
    if (!m_trayIcon) {
        return;
    }
    m_trayIcon->setIcon(QIcon::fromTheme("ukui-tool-symbolic", QIcon(TRAY_ICON)));
}

 void SideBarApplication::execShortcutAction(UkuiShortcut::PluginMetaType::PredefinedAction action)
 {
     switch (action) {
         default:
         case UkuiShortcut::PluginMetaType::NoAction:
             break;
         case UkuiShortcut::PluginMetaType::Hide: {
             if (m_sidebarEventBridge) {
                 m_sidebarEventBridge->hideSidebarQuickly();
                 QThread::msleep(600); //确保隐藏执行完毕
             }
             break;
         }
     }
 }

SidebarDbusService::SidebarDbusService(QObject *parent) : QObject(parent)
{
    QDBusConnection::sessionBus().unregisterService(SIDEBAR_CORE_DBUS_SERVICE);
    QDBusConnection::sessionBus().registerService(SIDEBAR_CORE_DBUS_SERVICE);
    //注册对象路径，导出所有此对象的插槽 ,registerObject参数：路径，interface,对象，options
    QDBusConnection::sessionBus().registerObject(SIDEBAR_CORE_DBUS_PATH, this,
                                                 QDBusConnection::ExportAllSlots|QDBusConnection::ExportAllSignals);
    m_sidebarApplication = qobject_cast<SideBarApplication*>(parent);     //动态映射
}

void SidebarDbusService::sidebarActive()
{
    m_sidebarApplication->m_sidebarEventBridge->activateSidebar();
}

void SidebarDbusService::showSidebar(QString arg)
{
    Q_UNUSED(arg)
    m_sidebarApplication->m_sidebarEventBridge->sidebarOnlyShow();
}

void SidebarDbusService::hideSidebar(QString arg)
{
    Q_UNUSED(arg)
    m_sidebarApplication->m_sidebarEventBridge->sidebarOnlyHide();
}

SidebarEventBridge::SidebarEventBridge(QObject *parent) : QObject(parent)
{
}

bool SidebarEventBridge::getSidebarState()
{
    return m_sidebarIsShowed;
}

void SidebarEventBridge::activateSidebar()
{
    Q_EMIT requestActivateSidebar();
}

void SidebarEventBridge::sidebarOnlyShow()
{
    Q_EMIT requestShowSidebar();
}

void SidebarEventBridge::sidebarOnlyHide()
{
    Q_EMIT requestHideSidebar();
}

void SidebarEventBridge::clearSidebarRedPoint(bool isShowed)
{
    m_sidebarIsShowed = isShowed;
    if (isShowed) {
        Q_EMIT sidebarRequestClearRedPoint();
    }
}

 void SidebarEventBridge::hideSidebarQuickly()
 {
    Q_EMIT requestHideQuickly();
 }

NotificationEventBridge::NotificationEventBridge(QObject *parent) : QObject(parent)
{
}

bool NotificationEventBridge::getNotificationCenterState()
{
    return m_notificationCenterIsShowed;
}

void NotificationEventBridge::notificationShow()
{
    Q_EMIT requestShowNotification();
}

void NotificationEventBridge::notificationHide()
{
    Q_EMIT requestHideNotification();
}

void NotificationEventBridge::clearNotificationCenterRedPoint(bool isShowed)
{
    m_notificationCenterIsShowed = isShowed;
    if (isShowed) {
        Q_EMIT notificationCenterRequestClearRedPoint();
    }
}
